package blog

import (
	"context"
	"encoding/json"
	"strconv"
)

const (
	Appname = "blog"
)

type Service interface {
	// 创建博客
	CreateBlog(context.Context, *CreateBlogRequest) (*Blog, error)
	// 查询文章列表, 列表查询, 没有必要查询文章的具体内容
	// 返回的是多个Blog，所以不能用*Blog
	QueryBlog(context.Context, *QueryBlogRequest) (*BlogSet, error)
	// 详情页, 尽量多的把关联的数据查询出来， content
	DescribeBlog(context.Context, *DescribeBlogRequest) (*Blog, error)
	// 修改文章状态
	UpdateBlogStatus(context.Context, *UpdateBlogStatusRequest) (*Blog, error)
	// 更新文章
	UpdateBlog(context.Context, *UpdateBlogRequest) (*Blog, error)
	// 删除文章
	DeleteBlog(context.Context, *DeleteBlogRequest) error
	// 文章审核, 审核通过的文章才能被看到
	AuditBlog(context.Context, *AuditBlogRequest) (*Blog, error)
}

type UpdateBlogStatusRequest struct {
	// 如果定义一篇文章, 使用对象Id, 具体的某一篇文章
	BlogId int64 `json:"blog_id"`
	// 修改的状态: DRAFT/PUBLISHED
	Status Status `json:"status"`
}

type UpdateBlogRequest struct {
	// 使用对象Id, 具体到某一篇文章
	// 为什么要定义为string类型呢？  因为后面用到c.Param("id"),它的返回值为string
	BlogId string `json:"blog_id"`
	// blog的范围, 不是用户传递进来的, 是api接口层 自动填充
	// Scope *common.Scope `json:"scope"`
	// 更新方式 区分全量更新/部分更新
	UpdateMode UpdateMode `json:"update_mode"`
	// 用户传入新的参数，才会更新，所以嵌套CreateBlogRequest
	*CreateBlogRequest
}

func NewUpdatePUTBlogRequest(id string) *UpdateBlogRequest {
	return &UpdateBlogRequest{
		BlogId:     id,
		UpdateMode: UPDATE_MODE_PUT,
		// 如果不实例化CreateBlogRequest，会报空指针
		CreateBlogRequest: NewCreateBlogRequest(),
	}
}

func NewUpdatePATCHBlogRequest(id string) *UpdateBlogRequest {
	return &UpdateBlogRequest{
		BlogId:            id,
		UpdateMode:        UPDATE_MODE_PATCH,
		CreateBlogRequest: NewCreateBlogRequest(),
	}
}

type DeleteBlogRequest struct {
	BlogId string `json:"blog_id"`
}

func NewDeleteBlogRequest(id string) *DeleteBlogRequest {
	return &DeleteBlogRequest{
		BlogId: id,
	}
}

type QueryBlogRequest struct {
	// 一页的大小，也就是一页有多少行
	PageSize int `json:"page_size"`
	// 当前处于几页
	PageNumber int `json:"page_number"`
	/* 因为要根据blog状态来查询，所以要定义此属性；为什么用指针呢？
	因为要实现nil的查询条件，如果不用指针，只能表示0、1条件 */
	// nil 没有这个过滤条件,为默认条件，表示要查询所有的博客
	// 0   DRAFT 表示草稿状态
	// 1   PUBLISHED 表示已发布状态
	Status *Status `json:"status"`
	// 基于文章标题的关键字搜索
	Keywords string `json:"keywords"`
	// 查询属于哪些用户的博客
	Usernames []string `json:"Usernames"`
}

// 把字符串转为整型：strconv.ParseInt(c.Query("req.PageNumber"), 10, 64)
// 下面的方法更好理解
func (r *QueryBlogRequest) ParsePageSize(ps string) {
	// 关于strconv.ParseInt(ps, 10, 64)的解释
	/*strconv.ParseInt(ps, 10, 64)是用于将字符串形式的数字转换为int64类型的方法。
	其中，ps是需要转换的字符串，10是表示字符串中的数字是十进制的，
	64是表示转换后的数字应该被存储在一个int64类型的变量中。*/
	psInt, _ := strconv.ParseInt(ps, 10, 64)
	// err != nil时，psInt == 0
	// 错误代码 if err != nil {
	// 	r.PageSize = 10
	// }
	if psInt != 0 {
		r.PageSize = int(psInt)
	}
}

// func (q *QueryBlogRequest) ParsePageSize(s string) {
// 	i, err := strconv.Atoi(s)
// 	if err == nil {
// 		q.PageSize = i
// 	}
// }

func (r *QueryBlogRequest) ParsePageNumber(ps string) {
	// 关于strconv.ParseInt(ps, 10, 64)的解释
	/*strconv.ParseInt(ps, 10, 64)是用于将字符串形式的数字转换为int64类型的方法。
	其中，ps是需要转换的字符串，10是表示字符串中的数字是十进制的，
	64是表示转换后的数字应该被存储在一个int64类型的变量中。*/
	psInt, _ := strconv.ParseInt(ps, 10, 64)
	// err != nil时，psInt == 0
	// 错误代码 if err != nil {
	// 	r.PageSize = 10
	// }
	if psInt != 0 {
		r.PageNumber = int(psInt)
	}
}

// 把常量的值改为指针
func (q *QueryBlogRequest) SetStatus(s Status) {
	var a = s
	q.Status = &a
}

func NewQueryBlogRequest() *QueryBlogRequest {
	return &QueryBlogRequest{
		PageSize:   10,
		PageNumber: 1,
		Usernames:  []string{},
	}
}

/*关于query.Offset(参数).Limit(参数)，offset：跳过多少个 limite: 取多少个*/
// Offset()方法是用来计算query.Offset(参数)中的参数值
func (q *QueryBlogRequest) Offset() int {
	return q.PageSize * (q.PageNumber - 1)
}

type BlogSet struct {
	// 博客的总数，显示一共有多少条博客
	Total int64 `json:"total"`
	// 返回的一页的数据，显示一页中有多少个Blog
	// []*Blog和*[]Blog有啥区别呢？
	// []*Blog表示指针的切片：切片中指针的变化，值也就发生变化
	// *[]Blog表示切片的指针：切片中的值变化，指针不会发生改变，导致最终的值不变
	Items []*Blog `json:"items"`
}

// 扩展BlogSet方法，例如追加数据
func (b *BlogSet) Add(x ...*Blog) {
	b.Items = append(b.Items, x...)
}

func NewBlogSet() *BlogSet {
	return &BlogSet{
		Items: []*Blog{},
	}
}

func (b *BlogSet) String() string {
	dj, _ := json.Marshal(b)
	return string(dj)
}

type DescribeBlogRequest struct {
	BlogId string `json:"blog_id"`
}

func NewDescribeBlogRequest(id string) *DescribeBlogRequest {
	return &DescribeBlogRequest{
		BlogId: id,
	}
}

type AuditBlogRequest struct {
	// 审核的文章
	BlogId string `json:"blog_id"`
	// 是否审核成功
	IsAuditPass bool `json:"is_audit_pass"`
}

func NewAuditBlogRequest(id string) *AuditBlogRequest {
	return &AuditBlogRequest{
		BlogId: id,
	}
}
